package com.suning.sawp.web.filter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import redis.clients.jedis.Jedis;

import com.suning.framework.sedis.JedisAction;
import com.suning.framework.sedis.JedisClient;
import com.suning.framework.sedis.impl.JedisClientImpl;
import com.suning.ids.client.config.Configuration;
import com.suning.sawp.service.util.ConfigUtil;
import com.suning.sawp.service.util.DateUtils;

/**
 * 
 * 判断URL日访问频次<br> 
 * 〈功能详细描述〉
 *
 * @author 12061818
 * @see [相关类/方法]（可选）
 * @since [产品/模块版本] （可选）
 */
public class UrlTrafficFilter implements Filter {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(UrlTrafficFilter.class);
    
    /**
     * url访问限制zookeeper url配置节点名
     */
    private static final String URL_TRAFFIC_URL_KEY = "/config/sawp/urlTraffic";
    
    /**
     * 页面链接访问redis缓存key前缀
     */
    private static final String STAFF_URL_TRAFFIC = "com.suning.sawp:urlTraffic:";
    
    private static final String URL_TRAFFIC_ERROR_MSG = "{\"retFlag\":\"0\",\"errorCode\":\"0025\",\"errorMessage\":\"此链接已超过今日访问上限\"}";
    
    private JedisClient jedisClient;
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        ApplicationContext ac = WebApplicationContextUtils.getRequiredWebApplicationContext(
                filterConfig.getServletContext());
        if (null == jedisClient) {
            jedisClient = ac.getBean(JedisClientImpl.class);
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
            ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        String staffId =  httpRequest.getRemoteUser();
        
        boolean flag = true;
        // 取zookeeper中访问频次限制开关，默认关闭
        String urlTrafficFlagStr = ConfigUtil.getConfig("/config/sawp/config", "urlTrafficFlag", "false");
        boolean urlTrafficFlag = Boolean.valueOf(urlTrafficFlagStr);
        
        if (urlTrafficFlag) {
            // 取请求的地址
            final String uri = httpRequest.getServletPath();
            // 取URL访问频次限制的URL规则
            List<String> patterns = getUrlPatterns(URL_TRAFFIC_URL_KEY);
            if (Configuration.matching(uri, patterns)) {
                LOGGER.info("UrlTrafficFilter staffId:{}, uri: {} need validUrlTraffic", staffId, uri);
                // 取zookeeper中配置的URL访问次数限制规则
                Map<String, String> ruleMap = ConfigUtil.getConfigMap(URL_TRAFFIC_URL_KEY, null);
                // 如果没取到URL对应的限制次数，默认为0，拒绝此次访问
                int urlLimit = NumberUtils.isNumber(ruleMap.get(uri)) ?
                        Integer.parseInt(ruleMap.get(uri)) : 0;
                // 取redis中此工号访问接口的次数
                final String key = STAFF_URL_TRAFFIC + staffId;
                String urlCountStr = jedisClient.execute(new JedisAction<String>() {
                    @Override
                    public String doAction(Jedis jedis) {
                        return jedis.hget(key, uri);
                    }
                });
                int urlCount = NumberUtils.isNumber(urlCountStr) ? Integer.parseInt(urlCountStr) : 0;
                if (urlCount > urlLimit) {
                    // 超出约定的访问上限
                    LOGGER.error("UrlTrafficFilter staffId:{}, uri: {} beyond the upper limit", staffId, uri);
                    flag = false;
                } else {
                    // 此链接请求访问次数+1
                    jedisClient.execute(new JedisAction<String>() {
                        @Override
                        public String doAction(Jedis jedis) {
                            if (!jedis.exists(key)) {
                                jedis.hincrBy(key, uri, 1L);
                                // 设置失效时间
                                Date tmrZeroPoint = DateUtils.getZeroPoint(
                                        DateUtils.getNextDay(new Date(), 1));
                                jedis.expireAt(key, tmrZeroPoint.getTime() / 1000);
                            } else {
                                jedis.hincrBy(key, uri, 1L);
                            }
                            return null;
                        }
                    });
                }
            }
        }
        
        if (flag) {
            // 校验通过，走下一个filter
            chain.doFilter(httpRequest, httpResponse);
            return;
        } else {
            // 校验不通过
            httpResponse.setContentType("application/json;charset=utf-8");
            httpResponse.addHeader("Cache-Control", "no-cache");
            httpResponse.getOutputStream().write(URL_TRAFFIC_ERROR_MSG.getBytes("UTF-8"));
            httpResponse.getOutputStream().flush();
            httpResponse.getOutputStream().close();
        }
    }
    
    /**
     * 
     * 功能描述: 需要校验设备ID的链接规则列表<br>
     * 〈功能详细描述〉
     *
     * @return
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    private List<String> getUrlPatterns(String key) {
        List<String> patterns = new ArrayList<String>();
        Map<String, String> redirectUrlRuleMap = ConfigUtil.getConfigMap(key, null);
        if (null != redirectUrlRuleMap && !redirectUrlRuleMap.isEmpty()) {
            Set<Entry<String, String>> entries = redirectUrlRuleMap.entrySet();
            for (Entry<String, String> entry : entries) {
                patterns.add(entry.getKey());
            }
        }
        
        return patterns;
    }

    @Override
    public void destroy() {
        
    }
    
}
